home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Applications / Nuntius 1.2 / src / Nuntius / UBinDecoders.cp < prev    next >
Encoding:
Text File  |  1994-03-14  |  20.3 KB  |  837 lines  |  [TEXT/MPS ]

  1. // Copyright © 1993 Peter Speck (speck@dat.ruc.dk).  All rights reserved.
  2. // UBinDecoders.cp
  3.  
  4. #include "UBinDecoders.h"
  5. #include "FileTools.h"
  6. #include "Tools.h"
  7. #include "UPrefsDatabase.h"
  8.  
  9. #include <ErrorGlobals.h>
  10.  
  11. #pragma segment MyArticle
  12.  
  13. // errNoExtractor may be deleted now
  14.  
  15.  
  16. #define qDebugBinHexAscii qDebug & 0
  17. #define qDebugBinHexRun        qDebug & 0
  18.  
  19.  
  20. #if qDebug
  21. #define DD(x) fprintf(stderr, x)
  22. #else
  23. #define DD(x)
  24. #endif
  25.  
  26. #if qDebugExtract
  27. #define DDE(x) fprintf(stderr, x)
  28. #else
  29. #define DDE(x)
  30. #endif
  31.  
  32. #if qDebug
  33. short gDebugLineNo = 0;
  34. void DebugDumpLine(const CStr255 &text)
  35. {
  36.     fprintf(stderr, "Line %hd is '%s'\n", gDebugLineNo, (char*)text);
  37. }
  38. #else
  39. #define DebugDumpLine(x)
  40. #endif
  41.  
  42. //-------------------------------------------------------------------
  43. PBinaryDecoder::PBinaryDecoder()
  44. {
  45.     fOutBufferP = nil;
  46.     fOutBufferPosP = nil;
  47.     fFile = nil;
  48. }
  49.  
  50. void PBinaryDecoder::IBinaryDecoder(TFile *outFile)
  51. {
  52.     FailInfo fi;
  53.     if (fi.Try())
  54.     {
  55.         fOutBufferP = NewPermPtr(kOutBufferSize);
  56.         fOutBufferPosP = fOutBufferP;
  57.         fBytesFreeInOutBuffer = kOutBufferSize;
  58.         FailNIL(outFile);
  59.         fFile = outFile;
  60.         fi.Success();
  61.     }
  62.     else // fail
  63.     {
  64.         FreeIfPtrObject(this);
  65.     }
  66. }
  67.  
  68. PBinaryDecoder::~PBinaryDecoder()
  69. {
  70.     DisposeIfPtr(fOutBufferP);
  71. }
  72.  
  73. void PBinaryDecoder::FlushOutBuffer()
  74. {
  75.     long numBytes = fOutBufferPosP - fOutBufferP;
  76.     if (numBytes)
  77.     {
  78.         WriteASyncToFile(fPB, fFile, fOutBufferP, numBytes);
  79.         fOutBufferPosP -= numBytes;
  80.         fBytesFreeInOutBuffer += numBytes;
  81.     }
  82. }
  83.  
  84. void PBinaryDecoder::WriteBytes(const void *p, long noBytes)
  85. {
  86.     const char *fromP = (const char*)p;
  87.     while (noBytes)
  88.     {
  89.         long subBytes = Min(noBytes, fBytesFreeInOutBuffer);
  90.         BytesMove(fromP, fOutBufferPosP, subBytes);
  91.         noBytes -= subBytes;
  92.         fromP += subBytes;
  93.         fOutBufferPosP += subBytes;
  94.         fBytesFreeInOutBuffer -= subBytes;
  95.         if (!fBytesFreeInOutBuffer)
  96.             FlushOutBuffer();
  97.     }
  98. }
  99.  
  100. void PBinaryDecoder::OpenFork(Boolean dataFork)
  101. {
  102.     FlushOutBuffer();
  103.     FailOSErr(fFile->CloseDataFork());
  104.     if (dataFork)
  105.     {
  106.         FailOSErr(fFile->OpenDataFork(fsRdWrPerm));
  107.         return;
  108.     }
  109.     FSSpec spec;
  110.     fFile->GetFileSpec(spec);
  111.     short refNum;
  112.     FailOSErr(FSpOpenRF(spec, fsRdWrPerm, refNum));
  113.     fFile->fDataRefNum = refNum;
  114. }
  115.  
  116. void PBinaryDecoder::CloseFork()
  117. {
  118.     FlushOutBuffer();
  119.     FailOSErr(fFile->CloseFile());
  120. }
  121.  
  122. void PBinaryDecoder::Abort()
  123. {
  124. //@ should probably catch the errors here
  125.     FailOSErr(fFile->CloseDataFork());
  126.     if (FileExist(fFile))
  127.         FailOSErr(fFile->DeleteFile());
  128. }
  129.  
  130. void PBinaryDecoder::PostProcess()
  131. {
  132.     FlushOutBuffer();
  133.     FailOSErr(fFile->CloseDataFork());
  134. }
  135.  
  136. #if 0
  137. void PBinaryDecoder::DecodeLine(const CStr255 & /* line */)
  138. {
  139.     SubClassResponsibility();
  140. }
  141.  
  142. Boolean PBinaryDecoder::IsDoneNow()
  143. {
  144.     SubClassResponsibility();
  145.     return true;
  146. }
  147. #endif
  148. //========================================================================
  149. unsigned char uu_table_1_1[128] = {
  150.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  151.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  152.       0,   4,   8,  12,  16,  20,  24,  28,  32,  36,  40,  44,  48,  52,  56,  60,
  153.      64,  68,  72,  76,  80,  84,  88,  92,  96, 100, 104, 108, 112, 116, 120, 124,
  154.     128, 132, 136, 140, 144, 148, 152, 156, 160, 164, 168, 172, 176, 180, 184, 188,
  155.     192, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 248, 252,
  156.       0,   4,   8,  12,  16,  20,  24,  28,  32,  36,  40,  44,  48,  52,  56,  60,
  157.      64,  68,  72,  76,  80,  84,  88,  92,  96, 100, 104, 108, 112, 116, 120, 124};
  158.  
  159. unsigned char uu_table_1_2[128] = {
  160.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  161.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  162.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  163.       1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
  164.       2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,   2,
  165.       3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  166.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  167.       1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1,   1};
  168.  
  169. unsigned char uu_table_2_1[128] = {
  170.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  171.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  172.       0,  16,  32,  48,  64,  80,  96, 112, 128, 144, 160, 176, 192, 208, 224, 240,
  173.       0,  16,  32,  48,  64,  80,  96, 112, 128, 144, 160, 176, 192, 208, 224, 240,
  174.       0,  16,  32,  48,  64,  80,  96, 112, 128, 144, 160, 176, 192, 208, 224, 240,
  175.       0,  16,  32,  48,  64,  80,  96, 112, 128, 144, 160, 176, 192, 208, 224, 240,
  176.       0,  16,  32,  48,  64,  80,  96, 112, 128, 144, 160, 176, 192, 208, 224, 240,
  177.       0,  16,  32,  48,  64,  80,  96, 112, 128, 144, 160, 176, 192, 208, 224, 240};
  178.  
  179. unsigned char uu_table_2_2[128] = {
  180.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  181.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  182.       0,   0,   0,   0,   1,   1,   1,   1,   2,   2,   2,   2,   3,   3,   3,   3,
  183.       4,   4,   4,   4,   5,   5,   5,   5,   6,   6,   6,   6,   7,   7,   7,   7,
  184.       8,   8,   8,   8,   9,   9,   9,   9,  10,  10,  10,  10,  11,  11,  11,  11,
  185.      12,  12,  12,  12,  13,  13,  13,  13,  14,  14,  14,  14,  15,  15,  15,  15,
  186.       0,   0,   0,   0,   1,   1,   1,   1,   2,   2,   2,   2,   3,   3,   3,   3,
  187.       4,   4,   4,   4,   5,   5,   5,   5,   6,   6,   6,   6,   7,   7,   7,   7};
  188.  
  189. unsigned char uu_table_3_1[128] = {
  190.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  191.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  192.       0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,
  193.       0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,
  194.       0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,
  195.       0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,
  196.       0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,
  197.       0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192,   0,  64, 128, 192};
  198.  
  199. unsigned char uu_table_3_2[128] = {
  200.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  201.       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  202.       0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,
  203.      16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
  204.      32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
  205.      48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
  206.       0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,
  207.      16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31};
  208.  
  209. PUUDecoder::PUUDecoder()
  210. {
  211. //    inherited::Initialize();
  212.     fGotUUDelemiter = false;
  213.     fFirstUULineInBlock = true;
  214.     fState = kInsideIt;
  215. }
  216.  
  217. void PUUDecoder::IUUDecoder(TFile *outFile)
  218. {
  219.     IBinaryDecoder(outFile);
  220. }
  221.  
  222. PUUDecoder::~PUUDecoder()
  223. {
  224. }
  225.  
  226. Boolean PUUDecoder::IsDoneNow()
  227. {
  228. //@    return false; // nothing is certain with uuencode
  229.     return (fState == kAtEnd);
  230. }
  231.  
  232. void PUUDecoder::PostProcess()
  233. {
  234.     PBinaryDecoder::PostProcess();
  235.     if (fState != kAtEnd)
  236.         if (fGotUUDelemiter || fState != kInsideIt) // special case for bad posed uuencoded files
  237.             FailOSErr(errBadBinaryFile);
  238. }
  239.  
  240. void PUUDecoder::PrepareUU()
  241. {
  242.     OpenFork(true);
  243. }
  244.  
  245. void PUUDecoder::DecodeLine(const CStr255 &line)
  246. {
  247.     CStr255 text(line);
  248.     ProcessLine(text);
  249. }
  250.  
  251. void PUUDecoder::ProcessLine(CStr255 &text)
  252. {
  253.     // not const argument as I want to be able to add a few spaces
  254.     unsigned char len = text.Length(); // easier to read
  255.     char *p = (char*)&text[1];
  256.     char binBuffer[200];
  257.     switch (fState)
  258.     {
  259.         case kInsideIt:
  260.             if (fFirstUULineInBlock && *p != 'M')
  261.             {
  262.                 DDE("First line in block and non-'M' char as length -> kWaitForDelim\n");
  263.                 fState = kWaitForDelim;
  264.                 return;
  265.             }
  266. #if qDebug
  267.             gDebugLineNo++;
  268. #endif
  269. #if qDebugExtract
  270.             if (gDebugLineNo < 10)
  271.             {
  272.                 DebugDumpLine(text);
  273.             }
  274. #endif
  275.             if (!len)
  276.             {
  277.                 DD("empty line found -> ignored\n");
  278.                 return;
  279.             }
  280.             fFirstUULineInBlock = false;
  281.             if (*p == '`' && len == 1)
  282.             {
  283.                 DDE("non-standard ` end symbol found -> kAtEnd\n");
  284.                 fState = kAtEnd; // bad 'end' found in some uuencoded files
  285.                 return;
  286.             }
  287.             if (*p == 32 && len == 1)
  288.             {
  289.                 DDE("single-space real-way-to-terminate symbol found -> kAtEnd\n");
  290.                 fState = kAtEnd;
  291.                 return;
  292.             }
  293.             if (*p == 'E' && strncmp(p, "END", 3) == 0)
  294.             {
  295.                 if (len <= 30) // E == 53 bytes
  296.                 {
  297.                     DDE("END delemiter found -> kWaitForDelim\n");
  298.                     fGotUUDelemiter = true;
  299.                     fState = kWaitForDelim;
  300.                     return;
  301.                 }
  302.                 if (!IsValidUUEncodeLine(text))
  303.                 {
  304.                     DD("bogous 'END' separator found -> kWaitForDelim\n");
  305.                     DebugDumpLine(text);
  306.                     fGotUUDelemiter = true;
  307.                     fState = kWaitForDelim;
  308.                     return;
  309.                 }
  310.                 DD("got line starting with 'END', but found no invalid chars (line is used)\n");
  311.                 DebugDumpLine(text);
  312.             }
  313.             if (*p == 'e' && strncmp(p, "end", 3) == 0)
  314.             {
  315.                 DDE("lowercase end symbol found -> kAtEnd\n");
  316.                 fState = kAtEnd;
  317.                 return;
  318.             }
  319.             if (!IsValidUUEncodeLine(text))
  320.                 break;
  321.             ++p;
  322.             short index = 0;
  323.             unsigned char lineLen = text[1] - 32;
  324.             long triplets = short(lineLen + 2) / 3;
  325.             long rest = triplets;
  326.             while (rest > 0)
  327.             {
  328.                 unsigned char uc;
  329.                 uc = uu_table_1_1[*p++];
  330.                 uc += uu_table_1_2[*p];
  331.                 binBuffer[index++] = uc;
  332.                 uc = uu_table_2_1[*p++];
  333.                 uc += uu_table_2_2[*p];
  334.                 binBuffer[index++] = uc;
  335.                 uc = uu_table_3_1[*p++];
  336.                 uc += uu_table_3_2[*p++];
  337.                 binBuffer[index++] = uc;
  338.                 rest -= 1;
  339.             }
  340.             WriteBytes(binBuffer, lineLen);
  341.             break;
  342.  
  343.         case kWaitForDelim:
  344.             if (*p == 'B' && strncmp(p, "BEGIN" , 5) == 0)
  345.             {
  346.                 DDE("BEGIN found -> kInsideIt\n");
  347.                 fState = kInsideIt;
  348.                 fFirstUULineInBlock = true;
  349.             }
  350.             else if (len >= 9 && strncmp(p, " CUT HERE", 9) == 0)
  351.             {
  352.                 DDE("Buggy ' CUT HERE' found -> kInsideIt\n");
  353.                 fState = kInsideIt;
  354.                 fFirstUULineInBlock = true;
  355.             }
  356.             else if (*p == 'M' && (len >= 61 && len <= 63))
  357.             {
  358.                 DDE("UUencoded stuff begins without starting separator:\n");
  359.                 DebugDumpLine(text);
  360.                 fState = kInsideIt;
  361.                 fFirstUULineInBlock = true;
  362.                 ProcessLine(text);
  363.             }
  364.             break;
  365.  
  366.         case kAtEnd:
  367.             break;
  368.             
  369. #if qDebug
  370.         default:
  371.             fprintf(stderr, "Invalid fState == %ld\n", long(fState));
  372.             ProgramBreak(gEmptyString);
  373. #endif
  374.     }
  375. }
  376.  
  377. Boolean PUUDecoder::IsValidUUEncodeLine(CStr255 &text)
  378. {
  379.     unsigned char lineLen = text[1] - 32;
  380.     if (lineLen < 0 || lineLen >= 64)
  381.     {
  382.         DD("bad line: lineLen < 0 || lineLen >= 64 -> ignored\n");
  383.         DebugDumpLine(text);
  384.         return false; // line too long or too short
  385.     }
  386.     if (!lineLen)
  387.         return true;
  388.     long triplets = short(lineLen + 2) / 3;
  389.     long tripletsLen = triplets * 4;
  390.     unsigned char len = text.Length();
  391.     if (len > tripletsLen + 4)
  392.     {
  393.         DD("Too much garbage at end of line -> ignored\n");
  394.         DebugDumpLine(text);
  395.         return false;
  396.     }
  397.     if (tripletsLen > text.Length())
  398.     {
  399. #if qDebugExtract
  400.         fprintf(stderr, "Found too few chars in line (eaten spaces case)\n");
  401.         fprintf(stderr, " len = %ld, lineLen = %ld, tripletsLen = %ld, noTriplets = %ld\n", long(text.Length()), long(lineLen), tripletsLen, triplets);
  402.         DebugDumpLine(text);
  403. #endif            
  404.         while (tripletsLen > text.Length())
  405.             text += 32; // add eaten spaces
  406.     }
  407.     unsigned char *p = &text[2];
  408.     --len;
  409.     while (len > 0)
  410.     {
  411.         if (*p < 32 || *p > 96)
  412.         {
  413. #if qDebug
  414.             fprintf(stderr, "Found invalid char (ascii = %ld) -> line ignored\n", *p);
  415.             fprintf(stderr, "Line = %ld, char offset = %ld, '%s'\n", gDebugLineNo, p - &text[1], (char*)text);
  416.             DebugDumpLine(text);
  417. #endif
  418.             return false; // found invalid char
  419.         }
  420.         ++p;
  421.         --len;
  422.     }
  423.     return true;
  424. }
  425. //=========================================================================
  426. const char kBadBinHexChar = '†';
  427. unsigned char gBinHexTable[256];
  428. Boolean gBinHexTableInited = false;
  429.  
  430. PBinHexDecoder::PBinHexDecoder()
  431. {
  432. //    inherited::Initialize();
  433.     fBinaryBufferP = nil;
  434.     fBinaryBufferPosP = nil;
  435.     fBytesInBinaryBuffer = 0;
  436.     fRunFlag = false;
  437.     f6To8Index = 0;
  438.     fForkBytesLeft = 0;
  439.     fAsciiState = kAtStart;
  440.     fBinaryState = kInHeader;
  441. }
  442.  
  443. void PBinHexDecoder::IBinHexDecoder(TFile *outFile)
  444. {
  445.     IBinaryDecoder(outFile);
  446.     FailInfo fi;
  447.     if (fi.Try())
  448.     {
  449.         fAsciiState = kAtStart;
  450.         fBinaryState = kInHeader;
  451.         fBinaryBufferP = NewPermPtr(kBinHexBinaryBufferSize);
  452.         fBinaryBufferPosP = fBinaryBufferP;
  453.         fBytesInBinaryBuffer = 0;
  454.         if (!gBinHexTableInited)
  455.         {
  456.             gBinHexTableInited = true;
  457.             BlockSet(Ptr(gBinHexTable), 256, kBadBinHexChar);
  458.             const char *p = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
  459.             unsigned ch = 0;
  460.             while (*p)
  461.                 gBinHexTable[*p++] = ch++;
  462.         }            
  463.         fi.Success();
  464.     }
  465.     else
  466.     {
  467.         FreeIfPtrObject(this);
  468.     }
  469. }
  470.         
  471. PBinHexDecoder::~PBinHexDecoder()
  472. {
  473.     DisposeIfPtr(fBinaryBufferP);
  474. //    inherited::Free();
  475. }
  476.  
  477. Boolean PBinHexDecoder::IsDoneNow()
  478. {
  479.     return (fAsciiState == kAtEnd);
  480. }
  481.  
  482. void PBinHexDecoder::PostProcess()
  483. {
  484.     if (fAsciiState != kAtEnd || fBinaryState != kPastEnd)
  485.         FailOSErr(errBadBinHex);
  486.     if (fBytesInBinaryBuffer || fRunFlag || fForkBytesLeft)
  487.         FailOSErr(errBadBinHex);
  488.     PBinaryDecoder::PostProcess();
  489. }
  490.  
  491. void PBinHexDecoder::DecodeLine(const CStr255 &line)
  492. {
  493.     CStr255 text(line);
  494. #if qDebugBinHexAscii
  495.     fprintf(stderr, "Decoding line '%s'\n", (char*)text);
  496. #endif
  497.     // want to be able to modify it (punch a couple of holes)
  498.     unsigned char len = text.Length(); // easier to read
  499.     while (len && text[len] <= 32)
  500.         len--;
  501.     char *p = (char*)&text[1];
  502.     switch (fAsciiState)
  503.     {
  504.         case kAtStart:
  505.             if (len && strncmp(p, "(This file must be converted with BinHex 4.0", 40) == 0)
  506.             {
  507.                 DDE("Found '(This file must…' -> kWaitForStartColon\n");
  508.                 fAsciiState = kWaitForStartColon;
  509.             }
  510.             break;
  511.  
  512.         case kWaitForStartColon:
  513.             if (len && *p == ':')
  514.             {
  515.                 if (text[len] == ':')
  516.                 {
  517.                     DDE("Found starting and endning colon in line (shorty one) -> kAtEnd\n");
  518.                     fAsciiState = kAtEnd;
  519.                     text[len] = char(0);
  520.                 }
  521.                 else
  522.                 {
  523.                     DDE("Found starting colon -> kInsideIt\n");
  524.                     fAsciiState = kInsideIt;
  525.                     text += char(0);
  526.                 }
  527.                 ProcessAsciiLine(p + 1);
  528.             }
  529.             break;
  530.  
  531.         case kInsideIt:
  532.             if (len >= 1 && text[len] == ':')
  533.             {
  534.                 DDE("Found endning colon -> kAtEnd\n");
  535.                 fAsciiState = kAtEnd;
  536.                 text[len] = char(0);
  537.                 ProcessAsciiLine(p);
  538.             }
  539.             else if (len >= 15 && strncmp(p, "--- end of part", 15) == 0)
  540.             {
  541.                 DDE("Found '--- end of part' -> kWaitForDelim\n");
  542.                 fAsciiState = kWaitForDelim;
  543.             }
  544.             else
  545.             {
  546.                 text += char(0);
  547.                 ProcessAsciiLine(p);
  548.             }
  549.             break;
  550.  
  551.         case kWaitForDelim:
  552.             if (len == 3 && strncmp(p, "---", 3) == 0)
  553.             {
  554.                 DDE("Found '---' -> kInsideIt\n");
  555.                 fAsciiState = kInsideIt;
  556.             }
  557.             else if (len >= 4 && strncmp(p, "--- ", 4) == 0)
  558.             {
  559.                 DDE("Found '--- …' -> kInsideIt\n");
  560.                 fAsciiState = kInsideIt;
  561.             }
  562.             break;
  563.  
  564.         case kAtEnd:
  565.             break;
  566.             
  567. #if qDebug
  568.         default:
  569.             fprintf(stderr, "Invalid fAsciiState == %ld\n", long(fAsciiState));
  570.             ProgramBreak(gEmptyString);
  571. #endif
  572.     }
  573. }
  574.  
  575. void PBinHexDecoder::ProcessAsciiLine(char *p)
  576. {
  577.     while (*p)
  578.     {
  579.         ProcessAsciiChar( *(unsigned char*)p);
  580.         p++;
  581.     }
  582.     while (fBytesInBinaryBuffer)
  583.     {
  584.         switch (fBinaryState)
  585.         {
  586.             case kInHeader:
  587.                 if (!ProcessHeaderData())
  588.                     return;
  589.                 break;
  590.  
  591.             case kInDataFork:
  592.             case kInRsrcForm:
  593.                 if (!ProcessForkData())
  594.                     return;
  595.                 break;
  596.             
  597.             case kPastEnd:
  598.                 FailOSErr(errBadBinHex);
  599.                 
  600. #if qDebug
  601.             default:
  602.                 fprintf(stderr, "Invalid fBinaryState == %ld\n", long(fBinaryState));
  603.                 ProgramBreak(gEmptyString);
  604. #endif
  605.         }
  606.     } // while
  607. }
  608.  
  609. void PBinHexDecoder::ProcessAsciiChar(unsigned char ch)
  610. {
  611.     char sixBits = gBinHexTable[ch];
  612.     if (sixBits == kBadBinHexChar)
  613.         FailOSErr(errBadBinHex);
  614.     unsigned char outChar;
  615.     switch (f6To8Index)
  616.     {
  617.         case 0:
  618.             fAcc = sixBits << 2;
  619.             f6To8Index++;
  620.             return;
  621.             
  622.         case 1:
  623.             outChar = fAcc | (sixBits >> 4);
  624.             fAcc = sixBits << 4;
  625.             f6To8Index++;
  626.             break;
  627.         
  628.         case 2:
  629.             outChar = fAcc | (sixBits >> 2);
  630.             fAcc = sixBits << 6;
  631.             f6To8Index++;
  632.             break;
  633.         
  634.         case 3:
  635.             outChar = fAcc | sixBits;
  636.             f6To8Index = 0;
  637.             break;
  638.  
  639. #if qDebug
  640.         default:
  641.             fprintf(stderr, "Invalid f6To8Index == %ld\n", long(f6To8Index));
  642.             ProgramBreak(gEmptyString);
  643. #endif
  644.     }
  645. #if qDebugBinHexAscii & 0
  646.     fprintf(stderr, "outChar = %ld, fRunFlag = %ld, fLastChar = %ld\n", long(outChar), long(fRunFlag), long(fLastChar));
  647. #endif
  648.     if (fRunFlag)
  649.     {
  650.         if (outChar == 0)
  651.         {
  652. #if qDebugBinHexRun
  653.             fprintf(stderr, "RunFlag: got 0: stores 0x90\n");
  654. #endif
  655.             StoreBinaryChar(0x90);
  656.             fLastChar = 0x90;
  657.         }
  658.         else
  659.         {
  660. #if qDebugBinHexRun
  661.             fprintf(stderr, "RunFlag, got run-length = %ld, writes copies\n", long(outChar));
  662. #endif
  663.             for (short i = 2; i <= outChar; i++)
  664.                 StoreBinaryChar(fLastChar);
  665.         }
  666.         fRunFlag = false;
  667.     }
  668.     else if (outChar == 0x90)
  669.     {
  670. #if qDebugBinHexRun
  671.         fprintf(stderr, "Got run byte: setting RunFlag\n");
  672. #endif
  673.         fRunFlag = true;
  674.     }
  675.     else
  676.     {
  677.         fLastChar = outChar;
  678.         StoreBinaryChar(outChar);
  679.     }
  680. }
  681.  
  682. //---------------------------------------------
  683. void PBinHexDecoder::StoreBinaryChar(unsigned char ch)
  684. {
  685. #if qDebugBinHexAscii
  686.     if (ch >= 32)
  687.         fprintf(stderr, "stores '%c' = $%lx\n", ch, long(ch)&0xFF);
  688.     else
  689.         fprintf(stderr, "stores 0x%lx\n", long(ch)&0xFF);
  690. #endif
  691.     if (fBytesInBinaryBuffer >= kBinHexBinaryBufferSize)
  692.         FailOSErr(errBadBinHex); // just a check for not writing past buffer
  693.     *fBinaryBufferPosP++ = ch;
  694.     fBytesInBinaryBuffer++;
  695. }
  696.  
  697. void PBinHexDecoder::EatenBinaryData(long noBytes)
  698. {
  699.     if (noBytes == fBytesInBinaryBuffer)
  700.     {
  701.         fBytesInBinaryBuffer = 0;
  702.         fBinaryBufferPosP = fBinaryBufferP;
  703.     }
  704.     else
  705.     {
  706.         fBytesInBinaryBuffer -= noBytes;
  707.         BytesMove(fBinaryBufferP + noBytes, fBinaryBufferP, fBytesInBinaryBuffer);
  708.         fBinaryBufferPosP -= noBytes;
  709.     }
  710. }
  711. const short kBinHexInitialCRC = 0;
  712. //---------------------------------------------
  713. typedef struct BinHexInfo
  714. {
  715.     OSType fFileType;
  716.     OSType fCreator;
  717.     unsigned short fdFlags;
  718.     long fDataLength;
  719.     long fRsrcLength;
  720. };
  721.  
  722.  
  723. Boolean PBinHexDecoder::ProcessHeaderData()
  724. {
  725.     if (!fBytesInBinaryBuffer)
  726.         return false; // want something
  727.     short nameLen = *(unsigned char*)fBinaryBufferP;
  728.     if (fBytesInBinaryBuffer < 22 + nameLen)
  729.         return false; // want hole header
  730.     fCRC = kBinHexInitialCRC;
  731.     // extract header
  732.     Ptr p = fBinaryBufferP + 1;
  733.     CStr255 filename(p);
  734.     p += nameLen;
  735.     if (*p++)
  736.         FailOSErr(errBadBinHex);
  737.     BinHexInfo bhi;
  738.     BytesMove(p, &bhi, sizeof(bhi));
  739.     CalcCRC(fBinaryBufferP, 20 + nameLen);
  740.     EatenBinaryData(20 + nameLen);
  741.     CheckCRC();
  742.     // specify the file
  743.     fFile->fUsesDataFork = false;
  744.     fFile->fUsesRsrcFork = noResourceFork;
  745.     FSSpec spec;
  746.     gPrefs->GetSilentDirAliasPrefs('FBin', spec);
  747.     CopyCString2String(filename, spec.name);
  748.     fFile->Specify(spec);
  749.     fFile->fCreator = bhi.fCreator;
  750.     fFile->fFileType = bhi.fFileType;
  751.     const short invMask = (1 << 0) | (1 << 8); // see TB 09 - Finder Flags
  752.     fFinderFlags = bhi.fdFlags & ~invMask;
  753.     fDataLen = bhi.fDataLength;
  754.     fRsrcLen = bhi.fRsrcLength;
  755.     // open file
  756.     MakeFilenameUnique(fFile);
  757.     FailOSErr(fFile->CreateDataFork());
  758.     if (bhi.fDataLength)
  759.         OpenFork(true);
  760.     fCRC = kBinHexInitialCRC;
  761.     fForkBytesLeft = fDataLen;
  762.     fBinaryState = kInDataFork;
  763.     return true;
  764. }
  765.  
  766. Boolean PBinHexDecoder::ProcessForkData()
  767. {
  768.     if (fForkBytesLeft)
  769.     {
  770.         long numBytes = Min(fForkBytesLeft, fBytesInBinaryBuffer);
  771.         WriteBytes(fBinaryBufferP, numBytes);
  772.         CalcCRC(fBinaryBufferP, numBytes);
  773.         EatenBinaryData(numBytes);
  774.         fForkBytesLeft -= numBytes;
  775.         return true;
  776.     }
  777.     if (fBytesInBinaryBuffer < 2)
  778.         return false;
  779.     CheckCRC();
  780.     CloseFork();
  781.     if (fBinaryState == kInDataFork)
  782.     {
  783.         if (fRsrcLen)
  784.             OpenFork(false);
  785.         fCRC = kBinHexInitialCRC;
  786.         fBinaryState = kInRsrcForm;
  787.         fForkBytesLeft = fRsrcLen;
  788.         return true;
  789.     }
  790.     else
  791.     {
  792.         FInfo fndrInfo;
  793.         FailOSErr(fFile->GetFinderInfo(fndrInfo));
  794.         fndrInfo.fdFlags = fFinderFlags;
  795.         FailOSErr(fFile->SetFinderInfo(fndrInfo));
  796.         fBinaryState = kPastEnd;
  797.         return true;
  798.     }
  799. }
  800.  
  801. void PBinHexDecoder::CalcCRC(const void *p, long noBytes)
  802. {
  803.     short crc = fCRC;
  804.     const unsigned char *up = (const unsigned char *)p;
  805.     while (noBytes)
  806.     {
  807.         short byte = *up++;
  808.         noBytes--;
  809.         for (short i = 8; i; i--)
  810.         {
  811.             Boolean hasHiBit = (crc & 0x8000) != 0; // crc >> 15
  812.             crc <<= 1;
  813.             crc |= byte >> 7;
  814.             if (hasHiBit)
  815.                 crc ^= 0x1021;
  816.             byte <<= 1;
  817.             byte &= 0xff;
  818.         }
  819.     }
  820.     fCRC = crc;
  821. }
  822.  
  823. void PBinHexDecoder::CheckCRC()
  824. {
  825.     const short null_crc = 0;
  826.     CalcCRC(&null_crc, 2);
  827.     short file_crc = *(short*)fBinaryBufferP;
  828.     EatenBinaryData(2);
  829.     if (file_crc != fCRC)
  830.     {
  831. #if qDebug
  832.         fprintf(stderr, "Bad CRC: calc = $%lx, file_crc = $%lx\n", long(fCRC)&0xFFFF, long(file_crc)&0xFFFF);
  833. #endif
  834.         FailOSErr(errBadBinHex);
  835.     }
  836. }
  837.